# 插槽 slot
slot noun
/slɑːt/
a long, narrow hole, especially one for putting coins into or for fitting a separate piece into
为什么使用 slot
类似 USB 插槽,用于扩展,让使用者决定组件显示的内容。抽取共性,留有扩展。不需要通过状态来控制显示与否!类似接口
# 基本使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
</head>
<body>
<div id="app">
<my-component>
<button>666</button>
</my-component>
<hr />
<my-component>
<a href="#">QQ</a>
</my-component>
<hr />
<my-component> </my-component>
<hr />
<my-component>
<!-- 会把所有内容替换到插槽里 -->
<button>666</button>
<a href="#">QQ</a>
</my-component>
</div>
<template id="myComponent">
<div>
<h2>组件</h2>
<!-- 若没有<slot></slot>标签,则这个组件只能显示h2(当然可以自定义h2中的内容,此处不再赘述),
其他时候想添加一个button,添加一个img都不可以
但是当有了<slot></slot>标签后,则可以自定义显示的东西。还可以有默认值 -->
<slot>
<button>
默认插槽值
</button>
</slot>
<!-- 自闭合标签也行,但是没有默认值了 -->
<!-- <slot /> -->
</div>
</template>
<script src="/lib/vue.js"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
message: "Hello",
},
methods: {},
components: {
MyComponent: {
template: "#myComponent",
},
},
});
</script>
</body>
</html>
# 具名插槽
# 2.6.0 后语法
注意 v-slot 只能添加在 <template> 上,除非当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。这一点和已经废弃的 slot 不同。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 正常工作时显示 li -->
<current-user :user="outerUser"></current-user>
<!-- 代码不会正常工作,因为只有 <current-user> 组件可以访问到 user(props或data中,一般都会封装在props中) 而我们提供的内容是在父级渲染的。 -->
<!-- <current-user :user="outerUser">
{{ user.firstName }}
</current-user> -->
<current-user :user="outerUser">
<!-- 如下三种方式写都可以 -->
<!-- <template v-slot:default="props"> -->
<!-- <template v-slot="props"> -->
<!-- props 名称可以自定义,推荐使用 props,由于子组件传递过来的值一般都在 props 中。user 是在模版中定义的 attribute -->
<template #default="props">
{{ props.user.firstName }}
</template>
</current-user>
</div>
<template id="currentUser">
<div>
<!-- :user="user" 中第一个是作为 attribute 可以自定义(在使用时必须为该 attr),第二个为绑定的子组件中的 props 或 data 数据,一般为 props 的数据 -->
<slot :user="user">{{ user.lastName }}</slot>
</div>
</template>
<script src="/lib/vue.js"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
message: "Hello",
outerUser: {
firstName: "si",
lastName: "li",
},
},
methods: {},
components: {
CurrentUser: {
template: "#currentUser",
props: {
user: {
type: Object,
default() {
return {
firstName: "san",
lastName: "zhang",
};
},
required: true,
},
},
},
},
});
</script>
</body>
</html>
# 2.6.0 前语法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 正常工作时显示 li -->
<current-user :user="outerUser"></current-user>
<!-- 代码不会正常工作,因为只有 <current-user> 组件可以访问到 user(props或data中,一般都会封装在props中) 而我们提供的内容是在父级渲染的。 -->
<!-- <current-user :user="outerUser">
{{ user.firstName }}
</current-user> -->
<current-user :user="outerUser">
<!-- 如下两种方式写都可以(#name是v-slot的语法糖) -->
<!-- <template slot="default" slot-scope="props"> -->
<!-- props 名称可以自定义,推荐使用 props,由于子组件传递过来的值一般都在 props 中。user 是在模版中定义的 attribute -->
<template slot-scope="props">
{{ props.user.firstName }}
</template>
</current-user>
</div>
<template id="currentUser">
<div>
<!-- :user="user" 中第一个是作为 attribute 可以自定义(在使用时必须为该 attr),第二个为绑定的子组件中的 props 或 data 数据,一般为 props 的数据 -->
<slot :user="user">{{ user.lastName }}</slot>
</div>
</template>
<script src="/lib/vue.js"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
message: "Hello",
outerUser: {
firstName: "si",
lastName: "li",
},
},
methods: {},
components: {
CurrentUser: {
template: "#currentUser",
props: {
user: {
type: Object,
default() {
return {
firstName: "san",
lastName: "zhang",
};
},
required: true,
},
},
},
},
});
</script>
</body>
</html>
# 作用域插槽
# 编译作用域
父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 凡是在 Vue 实例管理的 el 中的,都是在 Vue 实例中 -->
<my-component v-show="isShow"> </my-component>
<hr />
</div>
<template id="myComponent">
<!-- 只有在组件作用域中写的,才算是组件作用域,在组件中 -->
<div>
<h2>组件</h2>
<button v-show="isShow">按钮</button>
</div>
</template>
<script src="/lib/vue.js"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
message: "Hello",
isShow: true,
},
methods: {},
components: {
MyComponent: {
template: "#myComponent",
data() {
return {
isShow: false,
};
},
},
},
});
</script>
</body>
</html>
# 作用域插槽—2.6.0后语法
让插槽内容能够访问子组件中才有的数据是很有用的,如下例子:
为了让 user 在父级的插槽内容中可用,我们可以将 user 作为slot元素的一个 attribute 绑定上去。
绑定在 <slot> 元素上的 attribute 被称为插槽 prop。现在在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 正常工作时显示 li -->
<current-user :user="outerUser"></current-user>
<!-- 代码不会正常工作,因为只有 <current-user> 组件可以访问到 user(props或data中,一般都会封装在props中) 而我们提供的内容是在父级渲染的。 -->
<!-- <current-user :user="outerUser">
{{ user.firstName }}
</current-user> -->
<current-user :user="outerUser">
<!-- 如下三种方式写都可以 -->
<!-- <template v-slot:default="props"> -->
<!-- <template v-slot="props"> -->
<!-- props 名称可以自定义,推荐使用 props,由于子组件传递过来的值一般都在 props 中。user 是在模版中定义的 attribute -->
<template #default="props">
{{ props.user.firstName }}
</template>
</current-user>
</div>
<template id="currentUser">
<div>
<!-- :user="user" 中第一个是作为 attribute 可以自定义(在使用时必须为该 attr),第二个为绑定的子组件中的 props 或 data 数据,一般为 props 的数据 -->
<slot :user="user">{{ user.lastName }}</slot>
</div>
</template>
<script src="/lib/vue.js"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
message: "Hello",
outerUser: {
firstName: "si",
lastName: "li",
},
},
methods: {},
components: {
CurrentUser: {
template: "#currentUser",
props: {
user: {
type: Object,
default() {
return {
firstName: "san",
lastName: "zhang",
};
},
required: true,
},
},
},
},
});
</script>
</body>
</html>
# 作用域插槽—2.6.0前语法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 正常工作时显示 li -->
<current-user :user="outerUser"></current-user>
<!-- 代码不会正常工作,因为只有 <current-user> 组件可以访问到 user(props或data中,一般都会封装在props中) 而我们提供的内容是在父级渲染的。 -->
<!-- <current-user :user="outerUser">
{{ user.firstName }}
</current-user> -->
<current-user :user="outerUser">
<!-- 如下两种方式写都可以(#name是v-slot的语法糖) -->
<!-- <template slot="default" slot-scope="props"> -->
<!-- props 名称可以自定义,推荐使用 props,由于子组件传递过来的值一般都在 props 中。user 是在模版中定义的 attribute -->
<template slot-scope="props">
{{ props.user.firstName }}
</template>
</current-user>
</div>
<template id="currentUser">
<div>
<!-- :user="user" 中第一个是作为 attribute 可以自定义(在使用时必须为该 attr),第二个为绑定的子组件中的 props 或 data 数据,一般为 props 的数据 -->
<slot :user="user">{{ user.lastName }}</slot>
</div>
</template>
<script src="/lib/vue.js"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
message: "Hello",
outerUser: {
firstName: "si",
lastName: "li",
},
},
methods: {},
components: {
CurrentUser: {
template: "#currentUser",
props: {
user: {
type: Object,
default() {
return {
firstName: "san",
lastName: "zhang",
};
},
required: true,
},
},
},
},
});
</script>
</body>
</html>
# 示例
父组件替换插槽的标签,但是内容由子组件提供。需求如下:
- 子组件中包括一数组(可以是父组件通过 props 传递的),如 pLanguages: ['Java', 'Python', 'Go', 'JavaScript', 'C']
- 需要在多个界面进行展示:
- 某些界面以水平方向展示
- 某些界面以列表形式展示
- 某些界面直接展示一个数组
- 内容在子组件中,希望父组件告诉如何展示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
</head>
<body>
<div id="app">
<my-component></my-component>
<my-component :languanges="outerLanguages">
<!-- <template v-slot:default="props"> -->
<!-- <template v-slot="props"> -->
<template #default="props">
{{props.languanges.join('-')}}
</template>
</my-component>
</div>
<template id="myComponent">
<div>
<slot :languanges="languanges">
<!-- slot默认值 -->
<ul>
<li v-for="lang in languanges">{{lang}}</li>
</ul>
</slot>
</div>
</template>
<script src="/lib/vue.js"></script>
<script>
const vm = new Vue({
el: "#app",
data() {
return {
outerLanguages: ["Java", "Python", "Go", "JavaScript", "C"],
};
},
methods: {},
components: {
MyComponent: {
template: "#myComponent",
props: {
languanges: {
type: Array,
default() {
return ["C", "C++"];
},
required: true,
},
},
},
},
});
</script>
</body>
</html>